go 坑之并发访问map

go 坑之并发访问map

并发的对一个map进行读写是有问题的,主要是写(ps: 并发的写map肯定是任何语言都不行的,真的傻)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
简易解决方法 
1.map 加锁
2. 复制出来一个单独的map
代码2 // cloneReq帮助函数 防止并发的协程 访问同一个map 造成panic
func cloneReq(req map[string]string) map[string]string {
clone := map[string]string{}
lock := sync.RWMutex{}
lock.RLock()
for key, value := range req {
clone[key] = value
}
lock.RUnlock()
return clone
}

go提供了一种叫map的数据结构,可以翻译成映射,对应于其他语言的字典、哈希表。借助map,可以定义一个键和值,然后可以从map中获取、设置和删除这个值,尤其适合数据查找的场景。但是map的使用有一定的限制,如果是在单个协程中读写map,那么不会存在什么问题,如果是多个协程并发访问一个map,有可能会导致程序退出,并打印下面错误信息:

1
fatal error: concurrent map read and map write

上面的这个错误不是每次都会遇到的,如果并发访问的协程数不大,遇到的可能性就更小了。例如下面的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

func main() {
Map := make(map[int]int)

for i := 0; i < 10; i++ {
go writeMap(Map, i, i)
go readMap(Map, i)
}

}

func readMap(Map map[int]int, key int) int {
return Map[key]
}

func writeMap(Map map[int]int, key int, value int) {
Map[key] = value
}

只循环了10次,产生了20个协程并发访问map,程序基本不会出错,但是如果将循环次数变大,比如10万,运行下面程序基本每次都会出错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

func main() {
Map := make(map[int]int)

for i := 0; i < 100000; i++ {
go writeMap(Map, i, i)
go readMap(Map, i)
}

}

func readMap(Map map[int]int, key int) int {
return Map[key]
}

func writeMap(Map map[int]int, key int, value int) {
Map[key] = value
}

go官方博客有如下说明:

Maps are not safe for concurrent use: it’s not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines, the accesses must be mediated by some kind of synchronization mechanism. One common way to protect maps is with sync.RWMutex.

大致意思就是说,并发访问map是不安全的,会出现未定义行为,导致程序退出。所以如果希望在多协程中并发访问map,必须提供某种同步机制,一般情况下通过读写锁sync.RWMutex实现对map的并发访问控制,将map和sync.RWMutex封装一下,可以实现对map的安全并发访问,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import "sync"

type SafeMap struct {
sync.RWMutex
Map map[int]int
}

func main() {
safeMap := newSafeMap(10)

for i := 0; i < 100000; i++ {
go safeMap.writeMap(i, i)
go safeMap.readMap(i)
}

}

func newSafeMap(size int) *SafeMap {
sm := new(SafeMap)
sm.Map = make(map[int]int)
return sm

}

func (sm *SafeMap) readMap(key int) int {
sm.RLock()
value := sm.Map[key]
sm.RUnlock()
return value
}

func (sm *SafeMap) writeMap(key int, value int) {
sm.Lock()
sm.Map[key] = value
sm.Unlock()
}

原链接:https://www.jianshu.com/p/10a998089486

感谢你的打赏哦!